package com.liziyi0914.morearmor.mixin;

import com.liziyi0914.morearmor.PlayerManager;
import com.liziyi0914.morearmor.armor.BaseArmor;
import com.liziyi0914.morearmor.data.DataManager;
import com.liziyi0914.morearmor.event.EventManager;
import com.liziyi0914.morearmor.event.entity.EntityClickEvent;
import com.liziyi0914.morearmor.event.entity.EntityDamageByEntityEvent;
import com.liziyi0914.morearmor.event.entity.EntityDamageEvent;
import com.liziyi0914.morearmor.event.entity.EntityMoveEvent;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.MovementType;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(PlayerEntity.class)
public abstract class PlayerEntityInject extends LivingEntity {
    @Shadow
    protected abstract void applyDamage(DamageSource source, float amount);

    @Shadow
    public double capeX;

    @Shadow public abstract boolean checkFallFlying();

    protected PlayerEntityInject(EntityType<? extends LivingEntity> entityType, World world) {
        super(entityType, world);
    }

    @Override
    public void move(MovementType movementType, Vec3d movement) {
        if (getEntityWorld() instanceof ServerWorld) {
            if (movement.x > 0 || movement.y > 0 || movement.z > 0) {
                final BlockPos from = getBlockPos();
                final BlockPos to = from.add(movement.x, movement.y, movement.z);
                final EntityMoveEvent moveEvent = EventManager.call(new EntityMoveEvent(this, (ServerWorld) getEntityWorld(), from, to));
                if (moveEvent.isCanceled()) return;
            }
        }
        super.move(movementType, movement);
    }

    @Override
    public boolean damage(DamageSource source, float amount) {
        final EntityDamageEvent call = EventManager.call(new EntityDamageEvent(this, source, amount));
        if (call.isCanceled()) {
            return true;
        }
        if (amount != call.getDamage()) {
            amount = call.getDamage();
        }
        if (source.getAttacker() instanceof LivingEntity ea) {
            if (getEntityWorld() instanceof ServerWorld) {
                final EntityDamageByEntityEvent call2 = EventManager.call(new EntityDamageByEntityEvent(this, ea, source, amount));
                return super.damage(source, (float) call2.getDamage());
            }
        }
        return super.damage(source, amount);
    }

    @Override
    public void swingHand(Hand hand, boolean fromServerPlayer) {
        super.swingHand(hand, fromServerPlayer);
        //noinspection ConstantConditions
        if (((LivingEntity) this) instanceof ServerPlayerEntity player) {
            EventManager.call(new EntityClickEvent(player, true, null, null, getMainHandStack(), isSneaking()));
        }
    }

    @Inject(
            method = "tick",
            at = @At("HEAD")
    )
    private void tick(CallbackInfo ci) {
        DataManager.clear(this);

        Item helmet = this.getEquippedStack(EquipmentSlot.HEAD).getItem();
        Item chest = this.getEquippedStack(EquipmentSlot.CHEST).getItem();
        Item leggings = this.getEquippedStack(EquipmentSlot.LEGS).getItem();
        Item boots = this.getEquippedStack(EquipmentSlot.FEET).getItem();

        if (helmet instanceof BaseArmor armor) {
            onArmor(armor);
        }
        if (chest instanceof BaseArmor armor) {
            onArmor(armor);
        }
        if (leggings instanceof BaseArmor armor) {
            onArmor(armor);
        }
        if (boots instanceof BaseArmor armor) {
            onArmor(armor);
        }

        //noinspection ConstantConditions
        if (((LivingEntity) this) instanceof ServerPlayerEntity entity) {
            PlayerManager.register(entity);
        }
    }

    private void onArmor(BaseArmor armor) {
        DataManager.put(getUuid(), armor);
    }
}
